#include "libcomm.h"

// Initialized static value.
//
const CommType CommType::Void;
const CommType CommType::Null = (void*)0;
const CommType CommType::Error = (void*)-1;

CommType::CommType(void){ m_t=CT_VOID; memset(&m_v,0,sizeof(m_v));}
CommType::CommType(const void* ptr){ m_t=CT_POINTER; m_v.p=(char*)ptr; }
CommType::CommType(float f){ m_t=CT_FLOAT; m_v.f = f; }
CommType::CommType(double lf){ m_t=CT_DOUBLE; m_v.lf = lf; }
CommType::CommType(short s){ m_t=CT_SHORT; m_v.s = s; }
CommType::CommType(int i){ m_t=CT_INT; m_v.i = i; }
CommType::CommType(long l){ m_t=CT_LONG; m_v.l = l; }
CommType::CommType(long long ll){ m_t=CT_LONGLONG; m_v.ll = ll; }
CommType::CommType(unsigned short us){ m_t=CT_USHORT; m_v.us = us; }
CommType::CommType(unsigned int ui){ m_t=CT_UINT; m_v.ui = ui; }
CommType::CommType(unsigned long ul){ m_t=CT_ULONG; m_v.ul = ul; }
CommType::CommType(unsigned long long ull){ m_t=CT_ULONGLONG; m_v.ull = ull; }
CommType::CommType(const Buffer& buffer){ Copy(buffer.get(),buffer.size(),CT_BUFFER);}
CommType::CommType(const void* buffer, size_t size){ Copy(buffer,size,CT_BUFFER);}
CommType::CommType(const char* str){ Copy(str,((NULL==str)?0:(strlen(str)+1)),CT_STRING);}
CommType::CommType(const String& str){ Copy(str.get(),str.size(),CT_STRING);}
CommType::CommType(const CommType& rhs){ Copy(rhs);}
CommType& CommType::operator=(const CommType& rhs) 
{ 
	if(rhs.get() != this->get())
	{
		Release();
		Copy(rhs);
	}
	return *this;
}
void CommType::Copy(const void* buffer, size_t size, ValueType type)
{
	MYLIB_ASSERT_NOTNULL(CT_BUFFER == type || CT_STRING == type);
	m_t = type; 
	m_p_size = size; 
	m_v.p = NULL;
	if(m_p_size > 0 && NULL != buffer)
	{
		m_v.p = (char*)Buffer::malloc(m_p_size);
		MYLIB_ASSERT_NOTNULL(m_v.p);
		memcpy(m_v.p,buffer,m_p_size);
	}
}
void CommType::Copy(const CommType& rhs)
{
	if(CT_BUFFER == rhs.m_t || CT_STRING == rhs.m_t)
	{
		Copy(rhs.get(),rhs.size(),rhs.m_t);
	}
	else
	{
		m_t = rhs.m_t;
		m_v = rhs.m_v;
	}
}
void CommType::Release()
{ 
	if(CT_BUFFER == m_t || CT_STRING == m_t)
	{
		Buffer::free(m_v.p);
		m_p_size = 0;
	}
	// set to CommType::Null.
	memset(&m_v,0,sizeof(m_v));
	m_t = CT_POINTER;
}
CommType::~CommType(){ Release(); }

CommType::ValueType CommType::Type() const{ return m_t;} 

const char* CommType::TypeString() const
{
	static const char* TYPESTRING[] = {
		"void",		"pointer",	"buffer",	"string",	"float",	"double",
		"short",	"int",		"long",		"long long",
		"unsigned short",	"unsigned int",	"unsigned long",	"unsigned long long"
	};
	return TYPESTRING[m_t];
}

void* CommType::ptr() const
{
	void* ptr = NULL;
	switch(m_t)
	{
	case CT_STRING:
	case CT_BUFFER:
	case CT_POINTER:
		ptr = (void*)m_v.p;
		break;
	case CT_FLOAT:
		ptr = (void*)&m_v.f;
		break;
	case CT_DOUBLE:
		ptr = (void*)&m_v.lf;
		break;
	case CT_SHORT:
		ptr = (void*)&m_v.s;
		break;
	case CT_INT:
		ptr = (void*)&m_v.i;
		break;
	case CT_LONG:
		ptr = (void*)&m_v.l;
		break;
	case CT_LONGLONG:
		ptr = (void*)&m_v.ll;
		break;
	case CT_USHORT:
		ptr = (void*)&m_v.us;
		break;
	case CT_UINT:
		ptr = (void*)&m_v.ui;
		break;
	case CT_ULONG:
		ptr = (void*)&m_v.ul;
		break;
	case CT_ULONGLONG:
		ptr = (void*)&m_v.ull;
		break;
	default:
		break;
	}
	return ptr;
}

void* CommType::get()
{
	return ptr();
}

const void* CommType::get() const
{
	return ptr();
}

size_t CommType::size() const
{
	switch(m_t)
	{
	case CT_STRING:
	case CT_BUFFER:
		return m_p_size;
	case CT_POINTER:
		return sizeof(m_v.p);
	case CT_FLOAT:
		return sizeof(m_v.f);
	case CT_DOUBLE:
		return sizeof(m_v.lf);

	case CT_SHORT:
		return sizeof(m_v.s);
	case CT_INT:
		return sizeof(m_v.i);
	case CT_LONG:
		return sizeof(m_v.l);
	case CT_LONGLONG:
		return sizeof(m_v.ll);

	case CT_USHORT:
		return sizeof(m_v.us);
	case CT_UINT:
		return sizeof(m_v.ui);
	case CT_ULONG:
		return sizeof(m_v.ul);
	case CT_ULONGLONG:
		return sizeof(m_v.ull);
	default:
		break;
	}
	return 0;
}

bool CommType::Equal(const CommType& rhs) const
{
	if(m_t != rhs.m_t)
		return false;

	switch(m_t)
	{
	case CT_VOID:
		return true;
	case CT_STRING:
		return (0==strcmp(m_v.p,rhs.m_v.p));
	case CT_BUFFER:
		return (this->size()==rhs.size() && 0==memcmp(m_v.p,rhs.m_v.p,rhs.size()));
	case CT_POINTER:
		return (m_v.p==rhs.m_v.p);
	case CT_FLOAT:
		return (m_v.f==rhs.m_v.f);
	case CT_DOUBLE:
		return (m_v.lf==rhs.m_v.lf);

	case CT_SHORT:
		return (m_v.s==rhs.m_v.s);
	case CT_INT:
		return (m_v.i==rhs.m_v.i);
	case CT_LONG:
		return (m_v.l==rhs.m_v.l);
	case CT_LONGLONG:
		return (m_v.ll==rhs.m_v.ll);

	case CT_USHORT:
		return (m_v.us==rhs.m_v.us);
	case CT_UINT:
		return (m_v.ui==rhs.m_v.ui);
	case CT_ULONG:
		return (m_v.ul==rhs.m_v.ul);
	case CT_ULONGLONG:
		return (m_v.ull==rhs.m_v.ull);
	default:
		break;
	}
	return false;
}

bool CommType::IsVoid() const
{
	return this->Equal(CommType::Void);
}

bool CommType::IsNull() const
{
	return this->Equal(CommType::Null);
}

bool CommType::IsError() const
{
	return this->Equal(CommType::Error);
}

bool CommType::operator==(const CommType& rhs) const 
{
	return this->Equal(rhs);
}

bool CommType::operator!=(const CommType& rhs) const 
{ 
	return !this->Equal(rhs);
}

CommType::operator String() const{ MYLIB_ASSERT(CT_STRING==m_t); return m_v.p;}
CommType::operator Buffer() const{ MYLIB_ASSERT(CT_BUFFER==m_t); return Buffer(m_v.p,m_p_size);}
CommType::operator void*(){ return get();}
CommType::operator const void*() const{ return get();}
CommType::operator char*(){ MYLIB_ASSERT(CT_STRING==m_t); return m_v.p;}
CommType::operator const char*() const{ MYLIB_ASSERT(CT_STRING==m_t); return m_v.p;}
CommType::operator float() const{ MYLIB_ASSERT(CT_FLOAT==m_t); return m_v.f;}
CommType::operator double() const
{ 
	MYLIB_ASSERT(CT_DOUBLE==m_t||CT_FLOAT==m_t); 
	switch(m_t)
	{
	case CT_FLOAT:
		return m_v.f;
	default:
		break;
	}
	return m_v.lf;
}
CommType::operator short() const{ MYLIB_ASSERT(CT_SHORT==m_t); return m_v.s;}
CommType::operator int() const
{ 
	MYLIB_ASSERT(CT_INT==m_t||CT_SHORT==m_t); 
	switch(m_t)
	{
	case CT_SHORT:
		return m_v.s;
	default:
		break;
	}
	return m_v.i;
}
CommType::operator long() const
{ 
	MYLIB_ASSERT(CT_LONG==m_t||CT_INT==m_t||CT_SHORT==m_t); 
	switch(m_t)
	{
	case CT_INT:
		return m_v.i;
	case CT_SHORT:
		return m_v.s;
	default:
		break;
	}
	return m_v.l;
}
CommType::operator long long() const
{ 
	MYLIB_ASSERT(CT_LONGLONG==m_t||CT_LONG==m_t||CT_INT==m_t||CT_SHORT==m_t); 
	switch(m_t)
	{
	case CT_LONG:
		return m_v.l;
	case CT_INT:
		return m_v.i;
	case CT_SHORT:
		return m_v.s;
	default:
		break;
	}
	return m_v.ll;
}
CommType::operator unsigned short() const{ MYLIB_ASSERT(CT_USHORT==m_t); return m_v.us;}
CommType::operator unsigned int() const
{ 
	MYLIB_ASSERT(CT_UINT==m_t||CT_USHORT==m_t); 
	switch(m_t)
	{
	case CT_USHORT:
		return m_v.us;
	default:
		break;
	}
	return m_v.ui;
}
CommType::operator unsigned long() const
{ 
	MYLIB_ASSERT(CT_ULONG==m_t||CT_UINT==m_t||CT_USHORT==m_t); 
	switch(m_t)
	{
	case CT_UINT:
		return m_v.ui;
	case CT_USHORT:
		return m_v.us;
	default:
		break;
	}
	return m_v.ul;
}
CommType::operator unsigned long long() const
{
	MYLIB_ASSERT(CT_ULONGLONG==m_t||CT_ULONG==m_t||CT_UINT==m_t||CT_USHORT==m_t); 
	switch(m_t)
	{
	case CT_ULONG:
		return m_v.ul;
	case CT_UINT:
		return m_v.ui;
	case CT_USHORT:
		return m_v.us;
	default:
		break;
	}
	return m_v.ull;
}

String CommType::ToString() const
{
	if(CT_STRING == m_t)
	{
		return m_v.p;
	}
	else if(CT_BUFFER == m_t)
	{
		if(NULL == m_v.p || m_p_size == 0)
			return NULL;
		for (size_t i = m_p_size-1;i>=0;i--)
		{
			if(m_v.p[i] == 0)
				return m_v.p; // have '\0' ended. 
		}
		MYLIB_ASSERT(!"can't convert no-zero-ended buffer to string!");
	}
	else
	{
		String str(32);
		switch(m_t)
		{
		case CT_POINTER:
			return str.sprintf("%p",(void*)m_v.p);
		case CT_FLOAT:
			return str.sprintf("%f",m_v.f);
		case CT_DOUBLE:
			return str.sprintf("%lf",m_v.lf);

		case CT_SHORT:
			return str.sprintf("%hd",m_v.s);
		case CT_INT:
			return str.sprintf("%d",m_v.i);
		case CT_LONG:
			return str.sprintf("%ld",m_v.l);
		case CT_LONGLONG:
			return str.sprintf("%lld",m_v.ll);

		case CT_USHORT:
			return str.sprintf("%hu",m_v.us);
		case CT_UINT:
			return str.sprintf("%u",m_v.ui);
		case CT_ULONG:
			return str.sprintf("%lu",m_v.ul);
		case CT_ULONGLONG:
			return str.sprintf("%llu",m_v.ull);
		default:
			break;
		}
	}
	return NULL;
}

// "void*,long,unsigned long,size_t" have different size on 32/64bit system.
Buffer CommType::Serialize() const
{
	const void* ptr = this->get();
	ValueType type_ = this->Type();
	size_t size_ = this->size();
	MYLIB_ASSERT(size_ == 0 || NULL != ptr);
	MYLIB_ASSERT((int)type_<=0x7F);
	MYLIB_ASSERT(size_<=COMMTYPE_MAX_SIZE); // must be less than max value of signed int.
	unsigned char type = type_;		
	unsigned int size = size_;		
	int type_size = sizeof(type);	// 1 bytes.
	int size_size = sizeof(size);	// 4 bytes even size_t in 64bit.
	if(CT_POINTER==type_)
	{
		ptr = &m_v.p;
		size = sizeof(m_v.p);
	}
	
	Buffer buffer(type_size + size_size + size);
	memcpy((char*)buffer,&type,type_size);				// 1 bytes type.
	memcpy((char*)buffer+type_size,&size,size_size);	// 4 bytes size.
	memcpy((char*)buffer+type_size+size_size,ptr,size);	// value buffer.
	return buffer;
}

CommType CommType::Deserialize(const void* buffer, size_t sz)
{
	unsigned char type;
	unsigned int size;
	unsigned int type_size = sizeof(type);	// 1 bytes.
	unsigned int size_size = sizeof(size);	// 4 bytes.
	const char* ptr = (const char*)buffer;
	size_t size_ = sz;
	if(size_<type_size+size_size)
	{
		LOGERR("%s(line %d) error: invalid buffer header(size:0x%x).",__FUNCTION__,__LINE__,size_);
		return CommType::Null;
	}

	memcpy(&type,ptr,type_size);
	ptr += type_size;
	memcpy(&size,ptr,size_size);
	ptr += size_size;
	if(type > 0x7F || size > COMMTYPE_MAX_SIZE) 
	{
		LOGERR("%s(line %d) error: invalid buffer header(type:%d,size:0x%x).",__FUNCTION__,__LINE__,type,size);
		return CommType::Null;
	}
	if(type_size+size_size+size != size_)
	{
		LOGERR("%s(line %d) error: invalid buffer format.",__FUNCTION__,__LINE__);
		return CommType::Null;
	}

	switch(type)
	{
	case CT_VOID:
		{
			if(size != 0) return CommType::Null;
			return CommType::Void;
		}
	case CT_STRING:
		{
			if(size == 0) return (char*)NULL;
			if(ptr[size-1] != 0) return CommType::Null; // not ended by '\0'.
			return ptr;
		}
	case CT_BUFFER:
		{
			return Buffer(ptr,size);
		}
	case CT_POINTER:
		{
			if(size!=sizeof(void*)) return CommType::Null;
			return *((void**)ptr);
		}
	case CT_FLOAT:
		{
			if(size != sizeof(float)) return CommType::Null;
			return *((float*)ptr);
		}
	case CT_DOUBLE:
		{
			if(size != sizeof(double)) return CommType::Null;
			return *((double*)ptr);
		}
	case CT_SHORT:
		{
			if(size != sizeof(short)) return CommType::Null;
			return *((short*)ptr);
		}
	case CT_INT:
		{
			if(size != sizeof(int)) return CommType::Null;
			return *((int*)ptr);
		}
	case CT_LONG:
		{
			if(size != sizeof(long)) return CommType::Null;
			return *((long*)ptr);
		}
	case CT_LONGLONG:
		{
			if(size != sizeof(long long)) return CommType::Null;
			return *((long long*)ptr);
		}
	case CT_USHORT:
		{
			if(size != sizeof(unsigned short)) return CommType::Null;
			return *((unsigned short*)ptr);
		}
	case CT_UINT:
		{
			if(size != sizeof(unsigned int)) return CommType::Null;
			return *((unsigned int*)ptr);
		}
	case CT_ULONG:
		{
			if(size != sizeof(unsigned long)) return CommType::Null;
			return *((unsigned long*)ptr);
		}
	case CT_ULONGLONG:
		{
			if(size != sizeof(unsigned long long)) return CommType::Null;
			return *((unsigned long long*)ptr);
		}
	default:
		break;
	}

	return CommType::Null;
}

CommType CommType::Deserialize(const Buffer& buffer)
{
	return CommType::Deserialize(buffer.get(),buffer.size());
}







